home *** CD-ROM | disk | FTP | other *** search
/ Software Vault: The Diamond Collection / The Diamond Collection (Software Vault)(Digital Impact).ISO / cdr44 / frasrc19.zip / ZOOM.C < prev   
C/C++ Source or Header  |  1995-02-19  |  24KB  |  695 lines

  1. /*
  2.     zoom.c - routines for zoombox manipulation and for panning
  3.  
  4. */
  5.  
  6. #include <float.h>
  7. #include <stdio.h>
  8. #include <stdlib.h>
  9. #include <string.h>
  10. #include "fractint.h"
  11. #include "prototyp.h"
  12.  
  13. #define PIXELROUND 0.00001
  14.  
  15. static void _fastcall zmo_calc(double, double, double *, double *, double);
  16. static void _fastcall zmo_calcbf(bf_t,bf_t,bf_t,bf_t,bf_t,bf_t,bf_t,bf_t,bf_t);
  17. static int  check_pan(void);
  18. static void fix_worklist(void);
  19. static void _fastcall move_row(int fromrow,int torow,int col);
  20.  
  21. /* big number declarations */
  22. void calc_corner(bf_t target,bf_t p1,double p2,bf_t p3,double p4,bf_t p5)
  23. {
  24.    bf_t btmp1, btmp2 ,btmp3;
  25.    int saved; saved = save_stack();
  26.    btmp1 = alloc_stack(rbflength+2);
  27.    btmp2 = alloc_stack(rbflength+2);
  28.    btmp3 = alloc_stack(rbflength+2);
  29.    
  30.    /* use target as temporary variable */
  31.    floattobf(btmp3, p2);
  32.    mult_bf(btmp1,btmp3,p3);
  33.    mult_bf(btmp2,floattobf(target, p4),p5);
  34.    add_bf(target,btmp1,btmp2);
  35.    add_a_bf(target,p1);
  36.    restore_stack(saved);
  37. }
  38.  
  39. void drawbox(int drawit)
  40. {   
  41.     struct coords tl,bl,tr,br; /* dot addr of topleft, botleft, etc */
  42.     double tmpx,tmpy,dx,dy,rotcos,rotsin,ftemp1,ftemp2;
  43.     double fxwidth,fxskew,fydepth,fyskew,fxadj;
  44.     bf_t bffxwidth, bffxskew, bffydepth, bffyskew, bffxadj;
  45.     int saved; 
  46.     if (zwidth==0) { /* no box to draw */
  47.     if (boxcount!=0) { /* remove the old box from display */
  48.         clearbox();   /* asm routine */
  49.         boxcount = 0; }
  50.     reset_zoom_corners();
  51.     return; }
  52.     if(bf_math)
  53.     {
  54.        saved = save_stack();
  55.        bffxwidth = alloc_stack(rbflength+2);
  56.        bffxskew  = alloc_stack(rbflength+2);
  57.        bffydepth = alloc_stack(rbflength+2);
  58.        bffyskew  = alloc_stack(rbflength+2);
  59.        bffxadj   = alloc_stack(rbflength+2);
  60.     }
  61.     ftemp1 = PI*zrotate/72; /* convert to radians */
  62.     rotcos = cos(ftemp1);   /* sin & cos of rotation */
  63.     rotsin = sin(ftemp1);
  64.  
  65.     /* do some calcs just once here to reduce fp work a bit */
  66.     fxwidth = sxmax-sx3rd; 
  67.     fxskew  = sx3rd-sxmin;
  68.     fydepth = sy3rd-symax;
  69.     fyskew  = symin-sy3rd;
  70.     fxadj   = zwidth*zskew; 
  71.  
  72.     if(bf_math)
  73.     { 
  74.        /* do some calcs just once here to reduce fp work a bit */
  75.        sub_bf(bffxwidth,bfsxmax,bfsx3rd); 
  76.        sub_bf(bffxskew,bfsx3rd,bfsxmin);
  77.        sub_bf(bffydepth,bfsy3rd,bfsymax);
  78.        sub_bf(bffyskew,bfsymin,bfsy3rd);
  79.        floattobf(bffxadj, fxadj);
  80.     }
  81.  
  82.     /* calc co-ords of topleft & botright corners of box */
  83.     tmpx = zwidth/-2+fxadj; /* from zoombox center as origin, on xdots scale */
  84.     tmpy = zdepth*finalaspectratio/2;
  85.     dx = (rotcos*tmpx - rotsin*tmpy) - tmpx; /* delta x to rotate topleft */
  86.     dy = tmpy - (rotsin*tmpx + rotcos*tmpy); /* delta y to rotate topleft */
  87.  
  88.     /* calc co-ords of topleft */
  89.     ftemp1 = zbx + dx + fxadj; 
  90.     ftemp2 = zby + dy/finalaspectratio;
  91.  
  92.     tl.x   = (int)(ftemp1*(dxsize+PIXELROUND)); /* screen co-ords */
  93.     tl.y   = (int)(ftemp2*(dysize+PIXELROUND));
  94.     xxmin  = sxmin + ftemp1*fxwidth + ftemp2*fxskew; /* real co-ords */
  95.     yymax  = symax + ftemp2*fydepth + ftemp1*fyskew;
  96.     if(bf_math)
  97.     {
  98.        calc_corner(bfxmin,bfsxmin,ftemp1,bffxwidth,ftemp2,bffxskew);
  99.        calc_corner(bfymax,bfsymax,ftemp2,bffydepth,ftemp1,bffyskew);
  100.     }
  101.  
  102.     /* calc co-ords of bottom right */
  103.     ftemp1 = zbx + zwidth - dx - fxadj;
  104.     ftemp2 = zby - dy/finalaspectratio + zdepth;
  105.     br.x   = (int)(ftemp1*(dxsize+PIXELROUND));
  106.     br.y   = (int)(ftemp2*(dysize+PIXELROUND));
  107.     xxmax  = sxmin + ftemp1*fxwidth + ftemp2*fxskew;
  108.     yymin  = symax + ftemp2*fydepth + ftemp1*fyskew;
  109.     if(bf_math)
  110.     {
  111.        calc_corner(bfxmax,bfsxmin,ftemp1,bffxwidth,ftemp2,bffxskew);
  112.        calc_corner(bfymin,bfsymax,ftemp2,bffydepth,ftemp1,bffyskew);
  113.     }
  114.     /* do the same for botleft & topright */
  115.     tmpx = zwidth/-2 - fxadj;
  116.     tmpy = 0.0-tmpy;
  117.     dx = (rotcos*tmpx - rotsin*tmpy) - tmpx;
  118.     dy = tmpy - (rotsin*tmpx + rotcos*tmpy);
  119.     ftemp1 = zbx + dx - fxadj;
  120.     ftemp2 = zby + dy/finalaspectratio + zdepth;
  121.     bl.x   = (int)(ftemp1*(dxsize+PIXELROUND));
  122.     bl.y   = (int)(ftemp2*(dysize+PIXELROUND));
  123.     xx3rd  = sxmin + ftemp1*fxwidth + ftemp2*fxskew;
  124.     yy3rd  = symax + ftemp2*fydepth + ftemp1*fyskew;
  125.     if(bf_math)
  126.     {
  127.        calc_corner(bfx3rd,bfsxmin,ftemp1,bffxwidth,ftemp2,bffxskew);
  128.        calc_corner(bfy3rd,bfsymax,ftemp2,bffydepth,ftemp1,bffyskew);
  129.        restore_stack(saved);
  130.     }
  131.     ftemp1 = zbx + zwidth - dx + fxadj;
  132.     ftemp2 = zby - dy/finalaspectratio;
  133.     tr.x   = (int)(ftemp1*(dxsize+PIXELROUND));
  134.     tr.y   = (int)(ftemp2*(dysize+PIXELROUND));
  135.  
  136.     if (boxcount!=0) { /* remove the old box from display */
  137.     clearbox();   /* asm routine */
  138.     boxcount = 0; }
  139.  
  140.     if (drawit) { /* caller wants box drawn as well as co-ords calc'd */
  141. #ifndef XFRACT
  142.     /* build the list of zoom box pixels */
  143.     addbox(tl); addbox(tr);           /* corner pixels */
  144.     addbox(bl); addbox(br);
  145.     drawlines(tl,tr,bl.x-tl.x,bl.y-tl.y); /* top & bottom lines */
  146.     drawlines(tl,bl,tr.x-tl.x,tr.y-tl.y); /* left & right lines */
  147. #else
  148.         boxx[0] = tl.x + sxoffs;
  149.         boxy[0] = tl.y + syoffs;
  150.         boxx[1] = tr.x + sxoffs;
  151.         boxy[1] = tr.y + syoffs;
  152.         boxx[2] = br.x + sxoffs;
  153.         boxy[2] = br.y + syoffs;
  154.         boxx[3] = bl.x + sxoffs;
  155.         boxy[3] = bl.y + syoffs;
  156.         boxcount = 1;
  157. #endif
  158.     dispbox();                  /* asm routine to paint it */
  159.     }
  160.     }
  161.  
  162. void _fastcall drawlines(struct coords fr, struct coords to,
  163.                 int dx, int dy)
  164. {   int xincr,yincr,ctr;
  165.     int altctr,altdec,altinc;
  166.     struct coords tmpp,line1,line2;
  167.  
  168.     if (abs(to.x-fr.x) > abs(to.y-fr.y)) { /* delta.x > delta.y */
  169.     if (fr.x>to.x) { /* swap so from.x is < to.x */
  170.         tmpp = fr; fr = to; to = tmpp; }
  171.     xincr = (to.x-fr.x)*4/sxdots+1; /* do every 1st, 2nd, 3rd, or 4th dot */
  172.     ctr = (to.x-fr.x-1)/xincr;
  173.     altdec = abs(to.y-fr.y)*xincr;
  174.     altinc = to.x-fr.x;
  175.     altctr = altinc/2;
  176.     yincr = (to.y>fr.y)?1:-1;
  177.     line2.x = (line1.x = fr.x) + dx;
  178.     line2.y = (line1.y = fr.y) + dy;
  179.     while (--ctr>=0) {
  180.         line1.x += xincr;
  181.         line2.x += xincr;
  182.         altctr -= altdec;
  183.         while (altctr<0) {
  184.         altctr    += altinc;
  185.         line1.y += yincr;
  186.         line2.y += yincr;
  187.         }
  188.         addbox(line1);
  189.         addbox(line2);
  190.         }
  191.     }
  192.  
  193.     else { /* delta.y > delta.x */
  194.     if (fr.y>to.y) { /* swap so from.y is < to.y */
  195.         tmpp = fr; fr = to; to = tmpp; }
  196.     yincr = (to.y-fr.y)*4/sydots+1; /* do every 1st, 2nd, 3rd, or 4th dot */
  197.     ctr = (to.y-fr.y-1)/yincr;
  198.     altdec = abs(to.x-fr.x)*yincr;
  199.     altinc = to.y-fr.y;
  200.     altctr = altinc/2;
  201.     xincr = (to.x>fr.x) ? 1 : -1;
  202.     line2.x = (line1.x = fr.x) + dx;
  203.     line2.y = (line1.y = fr.y) + dy;
  204.     while (--ctr>=0) {
  205.         line1.y += yincr;
  206.         line2.y += yincr;
  207.         altctr  -= altdec;
  208.         while (altctr<0) {
  209.         altctr    += altinc;
  210.         line1.x += xincr;
  211.         line2.x += xincr;
  212.         }
  213.         addbox(line1);
  214.         addbox(line2);
  215.         }
  216.     }
  217.     }
  218.  
  219. void _fastcall addbox(struct coords point)
  220. {
  221.     point.x += sxoffs;
  222.     point.y += syoffs;
  223.     if (point.x >= 0 && point.x < sxdots && point.y >= 0 && point.y < sydots) {
  224.     boxx[boxcount] = point.x;
  225.     boxy[boxcount] = point.y;
  226.     ++boxcount;
  227.     }
  228.     }
  229.  
  230. void moveboxf(double dx, double dy)
  231. {   int align,row,col;
  232.     align = check_pan();
  233.     if (dx!=0.0) {
  234.     if ((zbx += dx) + zwidth/2 < 0)  /* center must stay onscreen */
  235.         zbx = zwidth/-2;
  236.     if (zbx + zwidth/2 > 1)
  237.         zbx = 1.0 - zwidth/2;
  238.     if (align != 0
  239.       && ((col = (int)(zbx*(dxsize+PIXELROUND))) & (align-1)) != 0) {
  240.         if (dx > 0) col += align;
  241.         col -= col & (align-1); /* adjust col to pass alignment */
  242.         zbx = (double)col/dxsize; }
  243.     }
  244.     if (dy!=0.0) {
  245.     if ((zby += dy) + zdepth/2 < 0)
  246.         zby = zdepth/-2;
  247.     if (zby + zdepth/2 > 1)
  248.         zby = 1.0 - zdepth/2;
  249.     if (align != 0
  250.       && ((row = (int)(zby*(dysize+PIXELROUND))) & (align-1)) != 0) {
  251.         if (dy > 0) row += align;
  252.         row -= row & (align-1);
  253.         zby = (double)row/dysize; }
  254.     }
  255.     }
  256.  
  257. static void _fastcall chgboxf(double dwidth, double ddepth)
  258. {
  259.     if (zwidth+dwidth > 1)
  260.     dwidth = 1.0-zwidth;
  261.     if (zwidth+dwidth < 0.05)
  262.     dwidth = 0.05-zwidth;
  263.     zwidth += dwidth;
  264.     if (zdepth+ddepth > 1)
  265.     ddepth = 1.0-zdepth;
  266.     if (zdepth+ddepth < 0.05)
  267.     ddepth = 0.05-zdepth;
  268.     zdepth += ddepth;
  269.     moveboxf(dwidth/-2,ddepth/-2); /* keep it centered & check limits */
  270.     }
  271.  
  272. void resizebox(int steps)
  273. {
  274.     double deltax,deltay;
  275.     if (zdepth*screenaspect > zwidth) { /* box larger on y axis */
  276.     deltay = steps * 0.036 / screenaspect;
  277.     deltax = zwidth * deltay / zdepth;
  278.     }
  279.     else {                /* box larger on x axis */
  280.     deltax = steps * 0.036;
  281.     deltay = zdepth * deltax / zwidth;
  282.     }
  283.     chgboxf(deltax,deltay);
  284.     }
  285.  
  286. void chgboxi(int dw, int dd)
  287. {   /* change size by pixels */
  288.     chgboxf( (double)dw/dxsize, (double)dd/dysize );
  289.     }
  290. #ifdef C6
  291. #pragma optimize("e",off)  /* MSC 6.00A messes up next rtn with "e" on */
  292. #endif
  293.  
  294. extern void show_three_bf();
  295.  
  296. static void _fastcall zmo_calcbf(bf_t bfdx, bf_t bfdy, 
  297.     bf_t bfnewx, bf_t bfnewy,bf_t bfplotmx1, bf_t bfplotmx2, bf_t bfplotmy1, 
  298.     bf_t bfplotmy2, bf_t bfftemp)
  299. {   
  300.     bf_t btmp1, btmp2, btmp3, btmp4, btempx, btempy ;
  301.     bf_t btmp2a, btmp4a;
  302.     int saved; saved = save_stack();
  303.        
  304.     btmp1  = alloc_stack(rbflength+2);
  305.     btmp2  = alloc_stack(rbflength+2);
  306.     btmp3  = alloc_stack(rbflength+2);
  307.     btmp4  = alloc_stack(rbflength+2);
  308.     btmp2a = alloc_stack(rbflength+2);
  309.     btmp4a = alloc_stack(rbflength+2);
  310.     btempx = alloc_stack(rbflength+2);
  311.     btempy = alloc_stack(rbflength+2);
  312.     
  313.     /* calc cur screen corner relative to zoombox, when zoombox co-ords
  314.        are taken as (0,0) topleft thru (1,1) bottom right */
  315.  
  316.     /* tempx = dy * plotmx1 - dx * plotmx2; */
  317.     mult_bf(btmp1,bfdy,bfplotmx1);
  318.     mult_bf(btmp2,bfdx,bfplotmx2);
  319.     sub_bf(btempx,btmp1,btmp2);
  320.  
  321.     /* tempy = dx * plotmy1 - dy * plotmy2; */
  322.     mult_bf(btmp1,bfdx,bfplotmy1);
  323.     mult_bf(btmp2,bfdy,bfplotmy2);
  324.     sub_bf(btempy,btmp1,btmp2);
  325.  
  326.     /* calc new corner by extending from current screen corners */
  327.     /* *newx = sxmin + tempx*(sxmax-sx3rd)/ftemp + tempy*(sx3rd-sxmin)/ftemp; */
  328.     sub_bf(btmp1,bfsxmax,bfsx3rd);
  329.     mult_bf(btmp2,btempx,btmp1);
  330.     /* show_three_bf("fact1",btempx,"fact2",btmp1,"prod ",btmp2,70); */
  331.     div_bf(btmp2a,btmp2,bfftemp);
  332.     /* show_three_bf("num  ",btmp2,"denom",bfftemp,"quot ",btmp2a,70); */
  333.     sub_bf(btmp3,bfsx3rd,bfsxmin);
  334.     mult_bf(btmp4,btempy,btmp3);
  335.     div_bf(btmp4a,btmp4,bfftemp);
  336.     add_bf(bfnewx,bfsxmin,btmp2a);
  337.     add_a_bf(bfnewx,btmp4a);
  338.  
  339.     /* *newy = symax + tempy*(sy3rd-symax)/ftemp + tempx*(symin-sy3rd)/ftemp; */
  340.     sub_bf(btmp1,bfsy3rd,bfsymax);
  341.     mult_bf(btmp2,btempy,btmp1);
  342.     div_bf(btmp2a,btmp2,bfftemp);
  343.     sub_bf(btmp3,bfsymin,bfsy3rd);
  344.     mult_bf(btmp4,btempx,btmp3);
  345.     div_bf(btmp4a,btmp4,bfftemp);
  346.     add_bf(bfnewy,bfsymax,btmp2a);
  347.     add_a_bf(bfnewy,btmp4a);
  348.     restore_stack(saved);
  349. }
  350.  
  351. static void _fastcall zmo_calc(double dx, double dy, double *newx, double *newy, double ftemp)
  352. {   
  353.     double tempx,tempy;
  354.     /* calc cur screen corner relative to zoombox, when zoombox co-ords
  355.        are taken as (0,0) topleft thru (1,1) bottom right */
  356.     tempx = dy * plotmx1 - dx * plotmx2;
  357.     tempy = dx * plotmy1 - dy * plotmy2;
  358.  
  359.     /* calc new corner by extending from current screen corners */
  360.     *newx = sxmin + tempx*(sxmax-sx3rd)/ftemp + tempy*(sx3rd-sxmin)/ftemp;
  361.     *newy = symax + tempy*(sy3rd-symax)/ftemp + tempx*(symin-sy3rd)/ftemp;
  362. }
  363.  
  364. void zoomoutbf(void) /* for ctl-enter, calc corners for zooming out */
  365. {   
  366.     /* (xxmin,yymax), etc, are already set to zoombox corners;
  367.        (sxmin,symax), etc, are still the screen's corners;
  368.        use the same logic as plot_orbit stuff to first calculate current screen
  369.        corners relative to the zoombox, as if the zoombox were a square with
  370.        upper left (0,0) and width/depth 1; ie calc the current screen corners
  371.        as if plotting them from the zoombox;
  372.        then extend these co-ords from current real screen corners to get
  373.        new actual corners
  374.        */
  375.     bf_t savbfxmin,savbfymax,bfftemp;
  376.     bf_t tmp1, tmp2, tmp3, tmp4, tmp5, tmp6,bfplotmx1,bfplotmx2,bfplotmy1,bfplotmy2;
  377.     int saved;
  378.     saved = save_stack();
  379.     savbfxmin = alloc_stack(rbflength+2);
  380.     savbfymax = alloc_stack(rbflength+2);
  381.     bfftemp   = alloc_stack(rbflength+2);
  382.     tmp1      = alloc_stack(rbflength+2);
  383.     tmp2      = alloc_stack(rbflength+2);
  384.     tmp3      = alloc_stack(rbflength+2);
  385.     tmp4      = alloc_stack(rbflength+2);
  386.     tmp5      = alloc_stack(rbflength+2);
  387.     tmp6      = alloc_stack(rbflength+2);
  388.     bfplotmx1 = alloc_stack(rbflength+2);
  389.     bfplotmx2 = alloc_stack(rbflength+2);
  390.     bfplotmy1 = alloc_stack(rbflength+2);
  391.     bfplotmy2 = alloc_stack(rbflength+2);
  392.     /* ftemp = (yymin-yy3rd)*(xx3rd-xxmin) - (xxmax-xx3rd)*(yy3rd-yymax); */
  393.     sub_bf(tmp1,bfymin,bfy3rd);
  394.     sub_bf(tmp2,bfx3rd,bfxmin);
  395.     sub_bf(tmp3,bfxmax,bfx3rd);
  396.     sub_bf(tmp4,bfy3rd,bfymax);
  397.     mult_bf(tmp5,tmp1,tmp2);
  398.     mult_bf(tmp6,tmp3,tmp4);
  399.     sub_bf(bfftemp,tmp5,tmp6);
  400.     /* plotmx1 = (xx3rd-xxmin); */ ; /* reuse the plotxxx vars is safe */
  401.     copy_bf(bfplotmx1,tmp2);
  402.     /* plotmx2 = (yy3rd-yymax); */ 
  403.     copy_bf(bfplotmx2,tmp4);
  404.     /* plotmy1 = (yymin-yy3rd); */
  405.     copy_bf(bfplotmy1,tmp1);
  406.     /* plotmy2 = (xxmax-xx3rd); */;
  407.     copy_bf(bfplotmy2,tmp3);
  408.  
  409.     /* savxxmin = xxmin; savyymax = yymax; */
  410.     copy_bf(savbfxmin,bfxmin); copy_bf(savbfymax,bfymax);
  411.  
  412.     sub_bf(tmp1,bfsxmin,savbfxmin); sub_bf(tmp2,bfsymax,savbfymax);
  413.     zmo_calcbf(tmp1,tmp2,bfxmin,bfymax,bfplotmx1,bfplotmx2,bfplotmy1,
  414.                bfplotmy2,bfftemp);
  415.     sub_bf(tmp1,bfsxmax,savbfxmin); sub_bf(tmp2,bfsymin,savbfymax);
  416.     zmo_calcbf(tmp1,tmp2,bfxmax,bfymin,bfplotmx1,bfplotmx2,bfplotmy1,
  417.                bfplotmy2,bfftemp);
  418.     sub_bf(tmp1,bfsx3rd,savbfxmin); sub_bf(tmp2,bfsy3rd,savbfymax);
  419.     zmo_calcbf(tmp1,tmp2,bfx3rd,bfy3rd,bfplotmx1,bfplotmx2,bfplotmy1,
  420.                bfplotmy2,bfftemp);
  421.     restore_stack(saved);
  422. }
  423.  
  424. void zoomoutdbl(void) /* for ctl-enter, calc corners for zooming out */
  425. {   
  426.    /* (xxmin,yymax), etc, are already set to zoombox corners;
  427.       (sxmin,symax), etc, are still the screen's corners;
  428.       use the same logic as plot_orbit stuff to first calculate current screen
  429.       corners relative to the zoombox, as if the zoombox were a square with
  430.       upper left (0,0) and width/depth 1; ie calc the current screen corners
  431.       as if plotting them from the zoombox;
  432.       then extend these co-ords from current real screen corners to get
  433.       new actual corners
  434.       */
  435.    double savxxmin,savyymax,ftemp;
  436.    ftemp = (yymin-yy3rd)*(xx3rd-xxmin) - (xxmax-xx3rd)*(yy3rd-yymax);
  437.    plotmx1 = (xx3rd-xxmin); /* reuse the plotxxx vars is safe */
  438.    plotmx2 = (yy3rd-yymax);
  439.    plotmy1 = (yymin-yy3rd);
  440.    plotmy2 = (xxmax-xx3rd);
  441.    savxxmin = xxmin; savyymax = yymax;
  442.    zmo_calc(sxmin-savxxmin,symax-savyymax,&xxmin,&yymax,ftemp);
  443.    zmo_calc(sxmax-savxxmin,symin-savyymax,&xxmax,&yymin,ftemp);
  444.    zmo_calc(sx3rd-savxxmin,sy3rd-savyymax,&xx3rd,&yy3rd,ftemp);
  445. }
  446.  
  447. void zoomout(void) /* for ctl-enter, calc corners for zooming out */
  448. {
  449.    if(bf_math)
  450.    {
  451.       zoomoutbf();
  452.    }
  453.    else
  454.       zoomoutdbl();
  455. }
  456.  
  457. #ifdef C6
  458. #pragma optimize("e",on)  /* back to normal */
  459. #endif
  460.  
  461. void aspectratio_crop(float oldaspect,float newaspect)
  462. {
  463.    double ftemp,xmargin,ymargin;
  464.    if (newaspect > oldaspect) { /* new ratio is taller, crop x */
  465.       ftemp = (1.0 - oldaspect / newaspect) / 2;
  466.       xmargin = (xxmax - xx3rd) * ftemp;
  467.       ymargin = (yymin - yy3rd) * ftemp;
  468.       xx3rd += xmargin;
  469.       yy3rd += ymargin;
  470.       }
  471.    else               { /* new ratio is wider, crop y */
  472.       ftemp = (1.0 - newaspect / oldaspect) / 2;
  473.       xmargin = (xx3rd - xxmin) * ftemp;
  474.       ymargin = (yy3rd - yymax) * ftemp;
  475.       xx3rd -= xmargin;
  476.       yy3rd -= ymargin;
  477.       }
  478.    xxmin += xmargin;
  479.    yymax += ymargin;
  480.    xxmax -= xmargin;
  481.    yymin -= ymargin;
  482. }
  483.  
  484. static int check_pan(void) /* return 0 if can't, alignment requirement if can */
  485. {   int i,j;
  486.     if (calc_status != 2 && calc_status != 4)
  487.     return(0); /* not resumable, not complete */
  488.     if ( curfractalspecific->calctype != StandardFractal
  489.       && curfractalspecific->calctype != calcmand
  490.       && curfractalspecific->calctype != calcmandfp
  491.       && curfractalspecific->calctype != lyapunov
  492.       && curfractalspecific->calctype != calcfroth)
  493.     return(0); /* not a worklist-driven type */
  494.     if (zwidth != 1.0 || zdepth != 1.0 || zskew != 0.0 || zrotate != 0.0)
  495.     return(0); /* not a full size unrotated unskewed zoombox */
  496.     /* can pan if we get this far */
  497.     if (calc_status == 4)
  498.     return(1); /* image completed, align on any pixel */
  499.     if (potflag && pot16bit)
  500.     return(1); /* 1 pass forced so align on any pixel */
  501.     if (stdcalcmode == 'b')
  502.     return(1); /* btm, align on any pixel */
  503.     if (stdcalcmode == 't')
  504.     return(0); /* tesselate, can't do it */
  505.     if (stdcalcmode != 'g' || (curfractalspecific->flags&NOGUESS)) {
  506.     if (stdcalcmode == '2' || stdcalcmode == '3') /* align on even pixel for 2pass */
  507.        return(2);
  508.     return(1); /* assume 1pass */
  509.     }
  510.     /* solid guessing */
  511.     start_resume();
  512.     get_resume(sizeof(int),&num_worklist,sizeof(worklist),worklist,0);
  513.     /* don't do end_resume! we're just looking */
  514.     i = 9;
  515.     for (j=0; j<num_worklist; ++j) /* find lowest pass in any pending window */
  516.     if (worklist[j].pass < i)
  517.         i = worklist[j].pass;
  518.     j = ssg_blocksize(); /* worst-case alignment requirement */
  519.     while (--i >= 0)
  520.     j = j>>1; /* reduce requirement */
  521.     return(j);
  522.     }
  523.  
  524. static void _fastcall move_row(int fromrow,int torow,int col)
  525. /* move a row on the screen */
  526. {   int startcol,endcol,tocol;
  527.     memset(dstack,0,xdots); /* use dstack as a temp for the row; clear it */
  528.     if (fromrow >= 0 && fromrow < ydots) {
  529.     tocol = startcol = 0;
  530.     endcol = xdots-1;
  531.     if (col < 0) {
  532.         tocol -= col;
  533.         endcol += col; }
  534.     if (col > 0)
  535.         startcol += col;
  536.     get_line(fromrow,startcol,endcol,(BYTE *)&dstack[tocol]);
  537.     }
  538.     put_line(torow,0,xdots-1,(BYTE *)dstack);
  539.     }
  540.  
  541. int init_pan_or_recalc(int do_zoomout) /* decide to recalc, or to chg worklist & pan */
  542. {   int i,j,row,col,y,alignmask,listfull;
  543.     if (zwidth == 0.0)
  544.     return(0); /* no zoombox, leave calc_status as is */
  545.     /* got a zoombox */
  546.     if ((alignmask=check_pan()-1) < 0) {
  547.     calc_status = 0; /* can't pan, trigger recalc */
  548.     return(0); }
  549.     if (zbx == 0.0 && zby == 0.0) {
  550.     clearbox();
  551.     return(0); } /* box is full screen, leave calc_status as is */
  552.     col = (int)(zbx*(dxsize+PIXELROUND)); /* calc dest col,row of topleft pixel */
  553.     row = (int)(zby*(dysize+PIXELROUND));
  554.     if (do_zoomout) { /* invert row and col */
  555.     row = 0-row;
  556.     col = 0-col; }
  557.     if ((row&alignmask) != 0 || (col&alignmask) != 0) {
  558.     calc_status = 0; /* not on useable pixel alignment, trigger recalc */
  559.     return(0); }
  560.     /* pan */
  561.     num_worklist = 0;
  562.     if (calc_status == 2) {
  563.        start_resume();
  564.        get_resume(sizeof(int),&num_worklist,sizeof(worklist),worklist,0);
  565.        } /* don't do end_resume! we might still change our mind */
  566.     /* adjust existing worklist entries */
  567.     for (i=0; i<num_worklist; ++i) {
  568.     worklist[i].yystart -= row;
  569.     worklist[i].yystop  -= row;
  570.     worklist[i].yybegin -= row;
  571.     worklist[i].xxstart -= col;
  572.     worklist[i].xxstop  -= col;
  573.     }
  574.     /* add worklist entries for the new edges */
  575.     listfull = i = 0;
  576.     j = ydots-1;
  577.     if (row < 0) {
  578.     listfull |= add_worklist(0,xdots-1,0,0-row-1,0,0,0);
  579.     i = 0 - row; }
  580.     if (row > 0) {
  581.     listfull |= add_worklist(0,xdots-1,ydots-row,ydots-1,ydots-row,0,0);
  582.     j = ydots - row - 1; }
  583.     if (col < 0)
  584.     listfull |= add_worklist(0,0-col-1,i,j,i,0,0);
  585.     if (col > 0)
  586.     listfull |= add_worklist(xdots-col,xdots-1,i,j,i,0,0);
  587.     if (listfull != 0) {
  588.     static FCODE msg[] = {"\
  589. Tables full, can't pan current image.\n\
  590. Cancel resumes old image, continue pans and calculates a new one."};
  591.     if (stopmsg(2,msg)) {
  592.         zwidth = 0; /* cancel the zoombox */
  593.         drawbox(1); }
  594.     else
  595.         calc_status = 0; /* trigger recalc */
  596.     return(0); }
  597.     /* now we're committed */
  598.     calc_status = 2;
  599.     clearbox();
  600.     if (row > 0) /* move image up */
  601.     for (y=0; y<ydots; ++y) move_row(y+row,y,col);
  602.     else     /* move image down */
  603.     for (y=ydots; --y>=0;)    move_row(y+row,y,col);
  604.     fix_worklist(); /* fixup any out of bounds worklist entries */
  605.     alloc_resume(sizeof(worklist)+10,1); /* post the new worklist */
  606.     put_resume(sizeof(int),&num_worklist,sizeof(worklist),worklist,0);
  607.     return(0);
  608.     }
  609.  
  610. static void _fastcall restart_window(int wknum)
  611. /* force a worklist entry to restart */
  612. {   int yfrom,yto,xfrom,xto;
  613.     if ((yfrom = worklist[wknum].yystart) < 0) yfrom = 0;
  614.     if ((xfrom = worklist[wknum].xxstart) < 0) xfrom = 0;
  615.     if ((yto = worklist[wknum].yystop) >= ydots) yto = ydots - 1;
  616.     if ((xto = worklist[wknum].xxstop) >= xdots) xto = xdots - 1;
  617.     memset(dstack,0,xdots); /* use dstack as a temp for the row; clear it */
  618.     while (yfrom <= yto)
  619.     put_line(yfrom++,xfrom,xto,(BYTE *)dstack);
  620.     worklist[wknum].sym = worklist[wknum].pass = 0;
  621.     worklist[wknum].yybegin = worklist[wknum].yystart;
  622. }
  623.  
  624. static void fix_worklist(void) /* fix out of bounds and symmetry related stuff */
  625. {   int i,j,k;
  626.     WORKLIST *wk;
  627.     for (i=0; i<num_worklist; ++i) {
  628.     wk = &worklist[i];
  629.     if ( wk->yystart >= ydots || wk->yystop < 0
  630.       || wk->xxstart >= xdots || wk->xxstop < 0) { /* offscreen, delete */
  631.         for (j=i+1; j<num_worklist; ++j)
  632.         worklist[j-1] = worklist[j];
  633.         --num_worklist;
  634.         --i;
  635.         continue; }
  636.     if (wk->yystart < 0) /* partly off top edge */
  637.         if ((wk->sym&1) == 0) /* no sym, easy */
  638.         wk->yystart = 0;
  639.         else { /* xaxis symmetry */
  640.         if ((j = wk->yystop + wk->yystart) > 0
  641.           && num_worklist < MAXCALCWORK) { /* split the sym part */
  642.             worklist[num_worklist] = worklist[i];
  643.             worklist[num_worklist].yystart = 0;
  644.             worklist[num_worklist++].yystop = j;
  645.             wk->yystart = j+1; }
  646.         else
  647.             wk->yystart = 0;
  648.         restart_window(i); /* restart the no-longer sym part */
  649.         }
  650.     if (wk->yystop >= ydots) { /* partly off bottom edge */
  651.        j = ydots-1;
  652.        if ((wk->sym&1) != 0) { /* uses xaxis symmetry */
  653.           if ((k = wk->yystart + (wk->yystop - j)) < j)
  654.          if (num_worklist >= MAXCALCWORK) /* no room to split */
  655.             restart_window(i);
  656.          else { /* split it */
  657.             worklist[num_worklist] = worklist[i];
  658.             worklist[num_worklist].yystart = k;
  659.             worklist[num_worklist++].yystop = j;
  660.             j = k-1; }
  661.           wk->sym &= -1 - 1; }
  662.        wk->yystop = j; }
  663.     if (wk->xxstart < 0) /* partly off left edge */
  664.         if ((wk->sym&2) == 0) /* no sym, easy */
  665.         wk->xxstart = 0;
  666.         else { /* yaxis symmetry */
  667.         if ((j = wk->xxstop + wk->xxstart) > 0
  668.           && num_worklist < MAXCALCWORK) { /* split the sym part */
  669.             worklist[num_worklist] = worklist[i];
  670.             worklist[num_worklist].xxstart = 0;
  671.             worklist[num_worklist++].xxstop = j;
  672.             wk->xxstart = j+1; }
  673.         else
  674.             wk->xxstart = 0;
  675.         restart_window(i); /* restart the no-longer sym part */
  676.         }
  677.     if (wk->xxstop >= xdots) { /* partly off right edge */
  678.        j = xdots-1;
  679.        if ((wk->sym&2) != 0) { /* uses xaxis symmetry */
  680.           if ((k = wk->xxstart + (wk->xxstop - j)) < j)
  681.          if (num_worklist >= MAXCALCWORK) /* no room to split */
  682.             restart_window(i);
  683.          else { /* split it */
  684.             worklist[num_worklist] = worklist[i];
  685.             worklist[num_worklist].xxstart = k;
  686.             worklist[num_worklist++].xxstop = j;
  687.             j = k-1; }
  688.           wk->sym &= -1 - 2; }
  689.        wk->xxstop = j; }
  690.     if (wk->yybegin < wk->yystart) wk->yybegin = wk->yystart;
  691.     if (wk->yybegin > wk->yystop)  wk->yybegin = wk->yystop;
  692.     }
  693.     tidy_worklist(); /* combine where possible, re-sort */
  694. }
  695.